Skip to content

fix(security): device consent phishing guard and 2FA disable throttle - #693

Merged
fclairamb merged 14 commits into
mainfrom
security/scan-specs-2026-10-27
Oct 27, 2026
Merged

fclairamb merged 14 commits into
mainfrom
security/scan-specs-2026-10-27

Conversation

@fclairamb

@fclairamb fclairamb commented Oct 27, 2026 •

Copy link
Copy Markdown
Owner

Specs filed from the daily security scans. Each spec is the tracker for its finding. This PR adds the specs and implements them.

What changed

  • V1: the device consent page now shows when the request was opened, the requester's IP and client, the approver's IP and a "different network" hint, a warning above Approve, and a required "I started this sign-in myself" checkbox when the code arrived pre-filled in the URL. The approver gets an email after approving. New migration 031_v0_43_0 stores the requester IP and user agent.
  • W1: DELETE /auth/2fa spends the shared per-user 2FA budget (5 per 15 minutes) before comparing the code, answers 429 RATE_LIMITED, and resets on success. The dash0 dialog shows the message, and no longer refreshes and retries on a wrong code.
  • Batch gate green: backend build, lint and tests, dash0 typecheck and lint, Playwright 980 passed, 15 skipped.

Decided by autopilot

  • V1, pre-filled code needs a second confirmation?: checkbox only, no retyping (spec's recommended answer).

Specs (now implemented, archived in specs/done/2026/10/)

  • V1 (medium): specs/done/2026/10/2026-10-27-01-device-consent-phishing-guard.md. The device-flow consent page approves a code anyone can mint without an account and send by link, already filled in. It shows no warning to approve only a sign-in you started yourself, and no detail about who opened the request. One click from a signed-in member gives the requester a 90-day PAT with the member's role. The fix adds the requester's details, a warning and an email notification, and keeps the CLI flow one click.
  • W1 (low): specs/done/2026/10/2026-10-27-02-disable-2fa-attempt-throttle.md. DELETE /auth/2fa checks the TOTP code without the per-user attempt budget that login 2FA uses. Someone holding a stolen session or PAT can guess the code at the global request rate and remove the second factor for good. The fix spends the shared 2FA budget before comparing.

Earlier scan specs (implemented, in specs/done/2026/10/)

  • U1 (low): 2026-10-26-02-ai-repair-hold-skips-param-only-secrets.md. The repair: auto hold applied only when the check's secrets map had a value. Fixed in fix(ai): hold every scheduled repair-auto run to its granted hosts #683.
  • S1 (medium): 2026-10-25-01-ai-repair-grant-dropped-by-reference-resolution.md. The repair grant was compared after ${…} references were resolved. Fixed in fix(ai): hold the repair: auto host grant when the script or env holds a reference #678; U1 closed the remaining gap.
  • R1 (medium): 2026-10-24-01-ai-repair-auto-apply-partial-runtime-host.md. repair: auto applied a candidate that kept a literal host and added one built at runtime. Fixed in feat(ai): hold auto-applied repairs to the previous version's hosts (+ scan specs) #663.
  • Q1 (medium): 2026-10-23-02-ai-loop-secret-relay-through-model.md. AI loops relayed an encoded secret through the model.
  • P1 (medium): 2026-10-22-01-ai-script-secret-host-guard-runtime.md. AI runs with real secrets were only host-checked by regex.
  • K1 (medium): 2026-10-20-01-sender-response-body-caps.md. Notification senders and RabbitMQ read responses uncapped.
  • J1 (medium): 2026-10-19-01-a2a-response-size-cap.md. A2A responses were read uncapped.
  • I1 (low): 2026-10-18-01-plugin-schema-file-ref.md. Plugin schema $ref could load local files.
  • G1/G2 (low): 2026-10-17-01-postgres-check-connection-config.md. Postgres conninfo escaping and fallback to the worker's PG* env.
  • F1: 2026-10-16-15-pat-cache-revocation.md. The PAT cache outlived revocation.
  • E1: 2026-10-16-14-totp-on-federated-sign-in.md. No TOTP on federated sign-in.
  • E2: 2026-10-16-13-atomic-auth-attempt-counters.md. Auth attempt counters were not atomic.
  • H3: 2026-10-16-12-mcp-tokens-rejected-on-rest.md. MCP OAuth tokens worked on REST.
  • H2: 2026-10-16-11-slack-signature-fail-closed.md. Slack signature check failed open.
  • T1 / M2 (high): 2026-10-16-10-discovery-scans-egress-guard.md. Discovery scans were outside the egress policy.
  • N4: 2026-10-16-09-js-check-nested-docker-guard.md. A JS docker sub-check bypassed the gate.
  • N3 (high): 2026-10-16-08-status-page-components-cross-org.md. Status-page components could be edited across orgs.
  • D2/D3 (high): 2026-10-16-07-guard-status-subscriber-and-publication-webhooks.md. Subscriber and publication webhooks were not guarded.
  • C3: 2026-10-16-06-accept-invite-existing-account.md. Accepting an invite logged in as an existing account.
  • C1: 2026-10-16-05-kubeconfig-exec-and-incluster.md. Kubeconfig exec and inCluster were allowed.
  • N2: 2026-10-16-04-mysql-dsn-injection.md. MySQL DSN injection.
  • N1: 2026-10-16-03-env-secret-reference-allowlist.md. No allowlist for ${env:} references.
  • C2/H1: 2026-10-16-02-token-purpose-separation.md. Purpose tokens were accepted as access tokens.
  • D1: 2026-10-16-01-microsoft-signin-unverified-email-linking.md. Microsoft nOAuth email linking.

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@codecov

codecov Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 95.50562% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 89.70%. Comparing base (0838916) to head (357506f).

Files with missing lines Patch % Lines
server/internal/handlers/auth/device_service.go 91.66% 4 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #693      +/-   ##
==========================================
- Coverage   89.73%   89.70%   -0.04%     
==========================================
  Files        1292     1292              
  Lines      131726   131787      +61     
==========================================
+ Hits       118199   118213      +14     
- Misses      13518    13565      +47     
  Partials        9        9              
Files with missing lines Coverage Δ
server/internal/db/models/device_auth.go 100.00% <ø> (ø)
server/internal/email/supportreply.go 92.68% <ø> (ø)
server/internal/handlers/auth/device_handler.go 80.28% <100.00%> (+1.49%) ⬆️
server/internal/handlers/auth/service.go 90.08% <100.00%> (+0.09%) ⬆️
server/internal/handlers/base/base.go 96.49% <100.00%> (+1.49%) ⬆️
server/internal/handlers/emailpreview/fixtures.go 100.00% <ø> (ø)
...internal/handlers/emailpreview/fixtures_account.go 100.00% <100.00%> (ø)
server/internal/middleware/ratelimit.go 85.37% <100.00%> (-0.97%) ⬇️
server/internal/handlers/auth/device_service.go 90.05% <91.66%> (+2.27%) ⬆️

... and 19 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

fclairamb and others added 12 commits October 27, 2026 10:06
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…roval

Device-code phishing guard (spec 2026-10-27-01, RFC 8628 §5.4):
- device_auth_requests stores requester_ip (trusted-proxy resolution, the
  rate limiter's rules, now shared as base.ExtractTrustedClientIP) and
  requester_user_agent (migration 031).
- GET /auth/device/consent returns requestedAt, requesterIp,
  requesterUserAgent (short summary) and approverIp.
- An approval enqueues device-token-created.html to the approver, naming
  the client, org, requester IP and the tokens page.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The authorize card now shows when, from which IP and with which client the
request was opened, the approver's own IP with a different-network hint, a
destructive warning above Approve, labels the client name as self-reported,
and requires an "I started this sign-in myself" tick when the code arrived
pre-filled from the URL (spec 2026-10-27-01).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…one can mint and send by link, with no warning

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Disable2FA checked the TOTP code with no attempt counter, so a stolen
session or PAT could guess its way to removing the second factor. It now
shares the per-user login budget (5 attempts per 15 minutes) and answers
429 RATE_LIMITED once it is spent, even for a valid code.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The disable dialog now shows the translated "too many attempts" message
on a 429. disable2FA also stops treating its 401 INVALID_2FA_CODE as an
expired session: the refresh-and-retry spent two attempts of the 2FA
budget per click and could sign the user out.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… with no attempt budget

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@fclairamb fclairamb changed the title chore(specs): security scan findings fix(security): device consent phishing guard and 2FA disable throttle Oct 27, 2026
@fclairamb
fclairamb merged commit 822186f into main Oct 27, 2026
34 checks passed
@fclairamb
fclairamb deleted the security/scan-specs-2026-10-27 branch October 27, 2026 14:36
fclairamb added a commit that referenced this pull request Oct 29, 2026
…eport screenshots (#708)

Fixes the findings of the daily security scans. Each spec is the tracker
for its finding.

## Fixed by this PR

- **Y1 (low)**:
`specs/done/2026/10/2026-10-29-01-bug-report-upload-instance-budget.md`.
Anonymous bug-report screenshots are capped at 100 MB per org, but the
caller picks the org, so the instance-wide total is 100 MB times the
number of orgs. Screenshots are also stored past the issue-dispatch cap
and never deleted. The fix adds an instance-wide budget for anonymous
screenshots, reserves the dispatch slot before storing, makes the quota
atomic and adds a retention.

## Earlier scan specs (implemented, in `specs/done/2026/10/`)

- **X1 (high)**:
`2026-10-28-01-pat-cannot-mint-sessions-or-sign-in-factors.md`. A PAT
could open sessions in its owner's other orgs and add sign-in factors.
Fixed in #699.
- **X2 (medium)**:
`2026-10-28-02-status-subscribe-throttle-and-keep-confirmed.md`.
Unthrottled subscribe confirm emails, and re-subscribing un-confirmed a
subscriber. Fixed in #699.
- **X3 (medium on SaaS)**:
`2026-10-28-03-invite-lookup-without-org-scan.md`. Invite lookup scanned
every org. Fixed in #699.
- **X4 (low)**: `2026-10-28-04-snmp-community-secret-field.md`. The SNMP
community was not a secret field. Fixed in #699.
- **V1 (medium)**: `2026-10-27-01-device-consent-phishing-guard.md`.
Device-flow consent phishing. Fixed in #693.
- **W1 (low)**: `2026-10-27-02-disable-2fa-attempt-throttle.md`. `DELETE
/auth/2fa` had no attempt budget. Fixed in #693.
- **U1 (low)**:
`2026-10-26-02-ai-repair-hold-skips-param-only-secrets.md`. The `repair:
auto` hold applied only when the check's `secrets` map had a value.
Fixed in #683.
- **S1 (medium)**:
`2026-10-25-01-ai-repair-grant-dropped-by-reference-resolution.md`. The
repair grant was compared after `${…}` references were resolved. Fixed
in #678; U1 closed the remaining gap.
- **R1 (medium)**:
`2026-10-24-01-ai-repair-auto-apply-partial-runtime-host.md`. `repair:
auto` applied a candidate that kept a literal host and added one built
at runtime. Fixed in #663.
- **Q1 (medium)**:
`2026-10-23-02-ai-loop-secret-relay-through-model.md`. AI loops relayed
an encoded secret through the model.
- **P1 (medium)**:
`2026-10-22-01-ai-script-secret-host-guard-runtime.md`. AI runs with
real secrets were only host-checked by regex.
- **K1 (medium)**: `2026-10-20-01-sender-response-body-caps.md`.
Notification senders and RabbitMQ read responses uncapped.
- **J1 (medium)**: `2026-10-19-01-a2a-response-size-cap.md`. A2A
responses were read uncapped.
- **I1 (low)**: `2026-10-18-01-plugin-schema-file-ref.md`. Plugin schema
`$ref` could load local files.
- **G1/G2 (low)**: `2026-10-17-01-postgres-check-connection-config.md`.
Postgres conninfo escaping and fallback to the worker's `PG*` env.
- **F1**: `2026-10-16-15-pat-cache-revocation.md`. The PAT cache
outlived revocation.
- **E1**: `2026-10-16-14-totp-on-federated-sign-in.md`. No TOTP on
federated sign-in.
- **E2**: `2026-10-16-13-atomic-auth-attempt-counters.md`. Auth attempt
counters were not atomic.
- **H3**: `2026-10-16-12-mcp-tokens-rejected-on-rest.md`. MCP OAuth
tokens worked on REST.
- **H2**: `2026-10-16-11-slack-signature-fail-closed.md`. Slack
signature check failed open.
- **T1 / M2 (high)**: `2026-10-16-10-discovery-scans-egress-guard.md`.
Discovery scans were outside the egress policy.
- **N4**: `2026-10-16-09-js-check-nested-docker-guard.md`. A JS `docker`
sub-check bypassed the gate.
- **N3 (high)**: `2026-10-16-08-status-page-components-cross-org.md`.
Status-page components could be edited across orgs.
- **D2/D3 (high)**:
`2026-10-16-07-guard-status-subscriber-and-publication-webhooks.md`.
Subscriber and publication webhooks were not guarded.
- **C3**: `2026-10-16-06-accept-invite-existing-account.md`. Accepting
an invite logged in as an existing account.
- **C1**: `2026-10-16-05-kubeconfig-exec-and-incluster.md`. Kubeconfig
`exec` and `inCluster` were allowed.
- **N2**: `2026-10-16-04-mysql-dsn-injection.md`. MySQL DSN injection.
- **N1**: `2026-10-16-03-env-secret-reference-allowlist.md`. No
allowlist for `${env:}` references.
- **C2/H1**: `2026-10-16-02-token-purpose-separation.md`. Purpose tokens
were accepted as access tokens.
- **D1**: `2026-10-16-01-microsoft-signin-unverified-email-linking.md`.
Microsoft nOAuth email linking.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
fclairamb added a commit that referenced this pull request Oct 30, 2026
🤖 I have created a release *beep* *boop*
---


##
[0.42.1](v0.42.0...v0.42.1)
(2026-10-30)


### Bug Fixes

* **checkicmp:** socket-open failure is an infrastructure error
([#712](#712))
([208ff0a](208ff0a))
* **dash0:** explain chart Full range switch and status page visibility
hint ([#702](#702))
([9c6141b](9c6141b))
* **deps:** update github.com/dop251/goja digest to 5f913d1
([#697](#697))
([146cb22](146cb22))
* **deps:** update github.com/dop251/goja digest to 85be141
([#706](#706))
([c6abd8e](c6abd8e))
* **deps:** update github.com/dop251/goja digest to d535508
([#696](#696))
([7529c15](7529c15))
* **deps:** update github.com/dop251/goja digest to f818168
([#709](#709))
([7d03f42](7d03f42))
* **deps:** update go dependencies (non-major)
([#690](#690))
([6de8ac7](6de8ac7))
* **deps:** update go dependencies (non-major)
([#694](#694))
([0838916](0838916))
* **deps:** update go dependencies (non-major)
([#698](#698))
([ea540e8](ea540e8))
* **deps:** update go dependencies (non-major) to v0.37.2
([#704](#704))
([67da7f7](67da7f7))
* **deps:** update module github.com/posthog/posthog-go to v1.39.0
([#711](#711))
([b2a7f2c](b2a7f2c))
* **deps:** update module github.com/prometheus/common to v0.73.0
([#703](#703))
([1ac4dc8](1ac4dc8))
* **deps:** update module github.com/slack-go/slack to v0.32.0
([#707](#707))
([74db3d2](74db3d2))
* ICMP capability for workers, escalation targets on events, login bug
report and degraded incident fixes
([#701](#701))
([9613f98](9613f98))
* **security:** device consent phishing guard and 2FA disable throttle
([#693](#693))
([822186f](822186f))
* **security:** instance-wide budget and retention for anonymous
bug-report screenshots
([#708](#708))
([2be7787](2be7787))
* **security:** PAT org binding, subscribe throttle, invite lookup, SNMP
secret ([#699](#699))
([cdf59bb](cdf59bb))
* **security:** scope on-call override deletion to its schedule and org
([#714](#714))
([6708f03](6708f03))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
fclairamb added a commit that referenced this pull request Oct 30, 2026
…rawl reports (#715)

Specs filed from the daily security scans. Each spec is the tracker for
its finding. This PR files the specs and implements Z1 and Z2.

## Implemented by this PR (specs archived in `specs/done/2026/10/`)

- **Z1 (low)**:
`specs/todos/2026-10-30-02-bug-report-authenticated-org-membership.md`.
Y1 residual. The screenshot budget added in #708 applies only to
anonymous reports. A signed-in user who is not a member of the named org
bypasses it and stores screenshots in any org. The fix treats a
non-member's report like an anonymous one.
- **Z2 (low)**:
`specs/todos/2026-10-30-03-report-retention-spares-crawl-reports.md`.
The bug-report retention sweep added in #708 matches the whole `reports`
storage group, so it also deletes website crawl reports older than 90
days. The same group also makes crawl reports count against the
bug-report quota. The fix restricts both to topic-less files.

## Earlier scan specs (implemented, in `specs/done/2026/10/`)

- **Y1 (low)**: `2026-10-29-01-bug-report-upload-instance-budget.md`.
Anonymous bug-report screenshots were capped per org, not per instance,
and never deleted. Fixed in #708; Z1 and Z2 are follow-ups.
- **X1 (high)**:
`2026-10-28-01-pat-cannot-mint-sessions-or-sign-in-factors.md`. A PAT
could open sessions in its owner's other orgs and add sign-in factors.
Fixed in #699.
- **X2 (medium)**:
`2026-10-28-02-status-subscribe-throttle-and-keep-confirmed.md`.
Unthrottled subscribe confirm emails, and re-subscribing un-confirmed a
subscriber. Fixed in #699.
- **X3 (medium on SaaS)**:
`2026-10-28-03-invite-lookup-without-org-scan.md`. Invite lookup scanned
every org. Fixed in #699.
- **X4 (low)**: `2026-10-28-04-snmp-community-secret-field.md`. The SNMP
community was not a secret field. Fixed in #699.
- **V1 (medium)**: `2026-10-27-01-device-consent-phishing-guard.md`.
Device-flow consent phishing. Fixed in #693.
- **W1 (low)**: `2026-10-27-02-disable-2fa-attempt-throttle.md`. `DELETE
/auth/2fa` had no attempt budget. Fixed in #693.
- **U1 (low)**:
`2026-10-26-02-ai-repair-hold-skips-param-only-secrets.md`. The `repair:
auto` hold applied only when the check's `secrets` map had a value.
Fixed in #683.
- **S1 (medium)**:
`2026-10-25-01-ai-repair-grant-dropped-by-reference-resolution.md`. The
repair grant was compared after `${…}` references were resolved. Fixed
in #678; U1 closed the remaining gap.
- **R1 (medium)**:
`2026-10-24-01-ai-repair-auto-apply-partial-runtime-host.md`. `repair:
auto` applied a candidate that kept a literal host and added one built
at runtime. Fixed in #663.
- **Q1 (medium)**:
`2026-10-23-02-ai-loop-secret-relay-through-model.md`. AI loops relayed
an encoded secret through the model.
- **P1 (medium)**:
`2026-10-22-01-ai-script-secret-host-guard-runtime.md`. AI runs with
real secrets were only host-checked by regex.
- **K1 (medium)**: `2026-10-20-01-sender-response-body-caps.md`.
Notification senders and RabbitMQ read responses uncapped.
- **J1 (medium)**: `2026-10-19-01-a2a-response-size-cap.md`. A2A
responses were read uncapped.
- **I1 (low)**: `2026-10-18-01-plugin-schema-file-ref.md`. Plugin schema
`$ref` could load local files.
- **G1/G2 (low)**: `2026-10-17-01-postgres-check-connection-config.md`.
Postgres conninfo escaping and fallback to the worker's `PG*` env.
- **F1**: `2026-10-16-15-pat-cache-revocation.md`. The PAT cache
outlived revocation.
- **E1**: `2026-10-16-14-totp-on-federated-sign-in.md`. No TOTP on
federated sign-in.
- **E2**: `2026-10-16-13-atomic-auth-attempt-counters.md`. Auth attempt
counters were not atomic.
- **H3**: `2026-10-16-12-mcp-tokens-rejected-on-rest.md`. MCP OAuth
tokens worked on REST.
- **H2**: `2026-10-16-11-slack-signature-fail-closed.md`. Slack
signature check failed open.
- **T1 / M2 (high)**: `2026-10-16-10-discovery-scans-egress-guard.md`.
Discovery scans were outside the egress policy.
- **N4**: `2026-10-16-09-js-check-nested-docker-guard.md`. A JS `docker`
sub-check bypassed the gate.
- **N3 (high)**: `2026-10-16-08-status-page-components-cross-org.md`.
Status-page components could be edited across orgs.
- **D2/D3 (high)**:
`2026-10-16-07-guard-status-subscriber-and-publication-webhooks.md`.
Subscriber and publication webhooks were not guarded.
- **C3**: `2026-10-16-06-accept-invite-existing-account.md`. Accepting
an invite logged in as an existing account.
- **C1**: `2026-10-16-05-kubeconfig-exec-and-incluster.md`. Kubeconfig
`exec` and `inCluster` were allowed.
- **N2**: `2026-10-16-04-mysql-dsn-injection.md`. MySQL DSN injection.
- **N1**: `2026-10-16-03-env-secret-reference-allowlist.md`. No
allowlist for `${env:}` references.
- **C2/H1**: `2026-10-16-02-token-purpose-separation.md`. Purpose tokens
were accepted as access tokens.
- **D1**: `2026-10-16-01-microsoft-signin-unverified-email-linking.md`.
Microsoft nOAuth email linking.

## What changed

- **Z1**: a signed-in user who is not a member of the named org (and not
a super admin) now spends the anonymous screenshot budget and needs a
dispatch slot. Text-only reports still go through.
- **Z2**: `ListFilesByGroup` and `SumFileSizeByGroup` only match
topic-less files, so retention no longer purges crawl reports and they
no longer use the screenshot quota.

## Decided by autopilot

- Z1 spec, open question (refuse a non-member with 403, or treat as
anonymous?): treat as anonymous (recommended answer).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
fclairamb added a commit that referenced this pull request Oct 31, 2026
…-in factors (#728)

## What changed

- **AA1 (medium)**: a JS check now has a 64 MiB per-run memory budget on
the native builtins that allocate in one call (`repeat`, `padStart/End`,
`join`, `Array(n)`, `ArrayBuffer` and typed arrays, `fill`,
`Array.from`, and the length-driven `sort`, `toSorted`, `map`, `with`,
`toReversed`, `toSpliced`). A refused call throws a `RangeError` and the
worker keeps running. Spec:
`specs/done/2026/10/2026-10-31-01-js-checker-memory-budget.md`.
- **AA2 (low)**: turning on TOTP, registering a passkey and setting a
first password now need the current password (or, without one, a sign-in
within 10 minutes). `2fa/confirm` requires a `setupToken`, and a
`totp-enabled` notice email is sent. dash0 asks for the password first.
Spec:
`specs/done/2026/10/2026-10-31-02-sign-in-factor-enrollment-reauth.md`.

**Breaking API change**: `2fa/setup` and `passkeys/register/begin`
refuse a request without `currentPassword` on accounts with a password;
`2fa/confirm` needs `setupToken`. Sessions issued before this have no
sign-in time, so passwordless accounts must sign in again once.

Known gaps (not covered): string `+` doubling, `replaceAll`,
`JSON.stringify` and `TextEncoder` can still amplify; the long-term fix
is process isolation of check workers (separate spec).

## Earlier scan specs (implemented, in `specs/done/2026/10/`)

- **Z1 (low)**:
`2026-10-30-02-bug-report-authenticated-org-membership.md`. Signed-in
non-members skipped the bug-report screenshot budget. Fixed in #715.
- **Z2 (low)**:
`2026-10-30-03-report-retention-spares-crawl-reports.md`. The report
retention sweep also deleted website crawl reports. Fixed in #715.
- **Y1 (low)**: `2026-10-29-01-bug-report-upload-instance-budget.md`.
Anonymous bug-report screenshots were capped per org, not per instance,
and never deleted. Fixed in #708; Z1 and Z2 are follow-ups.
- **X1 (high)**:
`2026-10-28-01-pat-cannot-mint-sessions-or-sign-in-factors.md`. A PAT
could open sessions in its owner's other orgs and add sign-in factors.
Fixed in #699.
- **X2 (medium)**:
`2026-10-28-02-status-subscribe-throttle-and-keep-confirmed.md`.
Unthrottled subscribe confirm emails, and re-subscribing un-confirmed a
subscriber. Fixed in #699.
- **X3 (medium on SaaS)**:
`2026-10-28-03-invite-lookup-without-org-scan.md`. Invite lookup scanned
every org. Fixed in #699.
- **X4 (low)**: `2026-10-28-04-snmp-community-secret-field.md`. The SNMP
community was not a secret field. Fixed in #699.
- **V1 (medium)**: `2026-10-27-01-device-consent-phishing-guard.md`.
Device-flow consent phishing. Fixed in #693.
- **W1 (low)**: `2026-10-27-02-disable-2fa-attempt-throttle.md`. `DELETE
/auth/2fa` had no attempt budget. Fixed in #693.
- **U1 (low)**:
`2026-10-26-02-ai-repair-hold-skips-param-only-secrets.md`. The `repair:
auto` hold applied only when the check's `secrets` map had a value.
Fixed in #683.
- **S1 (medium)**:
`2026-10-25-01-ai-repair-grant-dropped-by-reference-resolution.md`. The
repair grant was compared after `${…}` references were resolved. Fixed
in #678; U1 closed the remaining gap.
- **R1 (medium)**:
`2026-10-24-01-ai-repair-auto-apply-partial-runtime-host.md`. `repair:
auto` applied a candidate that kept a literal host and added one built
at runtime. Fixed in #663.
- **Q1 (medium)**:
`2026-10-23-02-ai-loop-secret-relay-through-model.md`. AI loops relayed
an encoded secret through the model.
- **P1 (medium)**:
`2026-10-22-01-ai-script-secret-host-guard-runtime.md`. AI runs with
real secrets were only host-checked by regex.
- **K1 (medium)**: `2026-10-20-01-sender-response-body-caps.md`.
Notification senders and RabbitMQ read responses uncapped.
- **J1 (medium)**: `2026-10-19-01-a2a-response-size-cap.md`. A2A
responses were read uncapped.
- **I1 (low)**: `2026-10-18-01-plugin-schema-file-ref.md`. Plugin schema
`$ref` could load local files.
- **G1/G2 (low)**: `2026-10-17-01-postgres-check-connection-config.md`.
Postgres conninfo escaping and fallback to the worker's `PG*` env.
- **F1**: `2026-10-16-15-pat-cache-revocation.md`. The PAT cache
outlived revocation.
- **E1**: `2026-10-16-14-totp-on-federated-sign-in.md`. No TOTP on
federated sign-in.
- **E2**: `2026-10-16-13-atomic-auth-attempt-counters.md`. Auth attempt
counters were not atomic.
- **H3**: `2026-10-16-12-mcp-tokens-rejected-on-rest.md`. MCP OAuth
tokens worked on REST.
- **H2**: `2026-10-16-11-slack-signature-fail-closed.md`. Slack
signature check failed open.
- **T1 / M2 (high)**: `2026-10-16-10-discovery-scans-egress-guard.md`.
Discovery scans were outside the egress policy.
- **N4**: `2026-10-16-09-js-check-nested-docker-guard.md`. A JS `docker`
sub-check bypassed the gate.
- **N3 (high)**: `2026-10-16-08-status-page-components-cross-org.md`.
Status-page components could be edited across orgs.
- **D2/D3 (high)**:
`2026-10-16-07-guard-status-subscriber-and-publication-webhooks.md`.
Subscriber and publication webhooks were not guarded.
- **C3**: `2026-10-16-06-accept-invite-existing-account.md`. Accepting
an invite logged in as an existing account.
- **C1**: `2026-10-16-05-kubeconfig-exec-and-incluster.md`. Kubeconfig
`exec` and `inCluster` were allowed.
- **N2**: `2026-10-16-04-mysql-dsn-injection.md`. MySQL DSN injection.
- **N1**: `2026-10-16-03-env-secret-reference-allowlist.md`. No
allowlist for `${env:}` references.
- **C2/H1**: `2026-10-16-02-token-purpose-separation.md`. Purpose tokens
were accepted as access tokens.
- **D1**: `2026-10-16-01-microsoft-signin-unverified-email-linking.md`.
Microsoft nOAuth email linking.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant